#! /usr/bin/php
<?php

// 定义注册的应用名和对应使用的协议
if(!isset($APP_NAMES)) {
    $APP_NAMES = [
        'App'=>'http',
        'RpcService'=>'rpc',
        'UdpService'=> 'udp',
        'WebsockService'=>'websocket',
    ];
}

// 定义环境
defined('SWOOLEFY_DEV') or define('SWOOLEFY_DEV','dev');
defined('SWOOLEFY_GRA') or define('SWOOLEFY_GRA','gra');
defined('SWOOLEFY_PRD') or define('SWOOLEFY_PRD','prd');
defined('SWOOLEFY_ENVS') or define('SWOOLEFY_ENVS', [
    SWOOLEFY_DEV,
    SWOOLEFY_GRA,
    SWOOLEFY_PRD,
]);

// 设置成中国时区
date_default_timezone_set('PRC');

// score目录
if(is_dir(__DIR__.'/vendor/bingcool/swoolefy')) {
    $SCORE_DIR = __DIR__.'/vendor/bingcool/swoolefy';
}else {
    $SCORE_DIR = __DIR__;
}

// 定义一个全局常量
defined('SCORE_DIR_ROOT') or define('SCORE_DIR_ROOT', $SCORE_DIR);
// 启动文件目录
defined('START_DIR_ROOT') or define('START_DIR_ROOT', __DIR__);

// include composer的自动加载类完成命名空间的注册
include_once START_DIR_ROOT.'/vendor/autoload.php';

// include App应用层的自定义的自动加载类命名空间
if(isset($argv[2]) && in_array(trim($argv[2]), array_keys($APP_NAMES))) {
    $app_name = trim($argv[2]);
    defined('APP_NAME') or define('APP_NAME', $app_name);
    if(trim($argv[1]) != 'create') {
        $autoloader_file = START_DIR_ROOT."/{$app_name}/autoloader.php";
        if(file_exists($autoloader_file)) {
            include_once $autoloader_file;
        }
    }
}else {
    if((isset($argv[1]) && $argv[1]=='help')|| (isset($argv[2]) && $argv[2]=='help')) {
    }else{
	    help('');
        write("[Error] app_name is not in APP_NAME array in swoolefy file, please check it");
	    return;
    }
    
}

// 判断当前环境,默认是dev(开发),gra(灰度),prd(生产)
$cli_params = array_flip($argv);

foreach(SWOOLEFY_ENVS as $env){
    if(isset($cli_params['-'.$env])){
        defined('SWOOLEFY_ENV') or define('SWOOLEFY_ENV', $env);
        break;
    }
}

defined('SWOOLEFY_ENV') or define('SWOOLEFY_ENV', SWOOLEFY_DEV);

function IS_DEV_ENV() {
    if(SWOOLEFY_ENV == SWOOLEFY_DEV) {
        return true;
    }
    return false;
}

function IS_GRA_ENV() {
    if(SWOOLEFY_ENV == SWOOLEFY_GRA) {
        return true;
    }
    return false;
}

function IS_PRD_ENV() {
    if(SWOOLEFY_ENV == SWOOLEFY_PRD) {
        return true;
    }
    return false;
}

function initCheck() {
    try{
       if(version_compare(phpversion(),'7.2.0','<')) {
            write("
                [error] php version must >= 7.2.0, current php version = ".phpversion()
            );
            exit();
        }
        if(version_compare(swoole_version(),'4.2.13','<')) {
            write("
                [error] the swoole version must >= 4.2.13, current swoole version = ".swoole_version()
            );
            exit();
        }
    }catch(Exception $e) {
        throw new Exception($e->getMessage(), 1);
    }
}

function opCacheClear(){
    if(function_exists('apc_clear_cache')){
        apc_clear_cache();
    }
    if(function_exists('opcache_reset')){
        opcache_reset();
    }
}

function commandParser() {
    global $argv;
    $command = isset($argv[1]) ? $argv[1] : null;
    $app_name = isset($argv[2]) ? $argv[2] : null;
    return ['command'=>$command, 'app_name'=>trim($app_name)];
}

function createProject($server_name) {
    $dirs = ['Config', 'Service'];
    $params = commandParser();
    $app_name = $params['app_name'];
    global $APP_NAMES;
    $app_root_dir = START_DIR_ROOT."/{$app_name}";
    if(is_dir($app_root_dir)) {
        write("You haved create {$app_name} project dir");
        exit();
    }

    $protocol = $APP_NAMES[$app_name];
    if(!$protocol) {
        write("[Error] app_name is not in APP_NAME array in swoolefy file, please check it");
        exit();
    }

    if($protocol == 'http') {
        $dirs = [
            'Config','Controller','Model','Module','View','Runtime','Log'
        ];
    }

    @mkdir($app_root_dir, 0777, true);
    foreach($dirs as $dir) {
        @mkdir($app_root_dir . '/' . $dir, 0777, true);
        switch($dir) {
            case 'Config':{
                $defines_file = $app_root_dir . '/' . $dir . '/defines.php';
                if(!file_exists($defines_file)) {
                    file_put_contents($defines_file, getDefines());
                }
                
                foreach(SWOOLEFY_ENVS as $env) {
                    $config_file = $app_root_dir . '/' . $dir . '/config-' . $env . '.php';
                    if(!file_exists($config_file)) {
                        file_put_contents($config_file, getDefultConfig());
                    }
                }
                break;
            }
            case 'Controller':{
                $controller_file = $app_root_dir . '/' . $dir.'/IndexController.php';
                if(!file_exists($controller_file)) {
                    file_put_contents($controller_file, getDefaultController());
                }
                break;
            }
            case 'Model': {
                $model_file = $app_root_dir . '/' . $dir.'/IndexModel.php';
                if(!file_exists($model_file)) {
                    file_put_contents($model_file, getDefaultModel());
                }
                break;
            }
            case 'View': {
                $view_controller_dir = $app_root_dir . '/' . $dir.'/Index';
                @mkdir($view_controller_dir, 0777, true);
                break;
            }
        }
    }
}

function startServer($server_name) {
    opCacheClear();
    global $APP_NAMES;
    $apps = $APP_NAMES;
    foreach($apps as $app_name => $protocol) {
        if($app_name == $server_name) {
            switch ($protocol) {
                case 'http':
                        startHttpService($app_name);
                    break;
                case 'websocket':
                        startWebsocket($app_name);
                    break;
                case 'rpc':
                        startRpc($app_name);
                    break;
                case 'udp':
                        startUdp($app_name);
                    break;
                default:
                    write("protocol is not in 【'http','websocket','rpc','udp'】");
                    break;
            }
        }
    }
    return ;
}

function startHttpService($app_name, $event_server_file_name = 'HttpServer') {
    $path = START_DIR_ROOT."/protocol/{$app_name}";
    if(!is_dir($path)) {
        @mkdir($path, 0777, true);
    }

    foreach(SWOOLEFY_ENVS as $env) {
        $config_file = $path."/config-{$env}.php";
        if(!file_exists($config_file)) {
            copy(SCORE_DIR_ROOT.'/score/Http/config.php', $config_file);
        }
    }

    $event_server_file = START_DIR_ROOT."/{$app_name}/{$event_server_file_name}.php";
    if(!file_exists($event_server_file)) {
        $search_str = "protocol\\http";
        $replace_str = "{$app_name}";
        $file_content_string = file_get_contents(SCORE_DIR_ROOT."/score/EventServer/{$event_server_file_name}.php");
        $count = 1;
        $file_content_string = str_replace($search_str, $replace_str, $file_content_string,$count);
        file_put_contents($event_server_file, $file_content_string);
    }

    $autoloader_file = START_DIR_ROOT."/{$app_name}/autoloader.php";
    if(!file_exists($autoloader_file)) {
        $search_str = "<{APP_NAME}>";
        $replace_str = "{$app_name}";
        $file_content_string = file_get_contents(SCORE_DIR_ROOT."/autoloader.php");
        $count = 1;
        $file_content_string = str_replace($search_str, $replace_str, $file_content_string,$count);
        file_put_contents($autoloader_file, $file_content_string);
        include_once $autoloader_file;
    }

    $config = include $path."/config-".SWOOLEFY_ENV.".php";

    if(isset($config['setting']['log_file'])) {
        $path = pathinfo($config['setting']['log_file'], PATHINFO_DIRNAME);
        if(!is_dir($path)) {
            mkdir($path, 0777, true);
        }
    }

    if(isDaemon()) {
        $config['setting']['daemonize'] = true;
    }

    if(!isset($config['app_conf'])) {
        write("Error:protocol/{$app_name}/config-".SWOOLEFY_ENV.".php"." must include app_conf file and set app_conf");
        exit();
    }

    setEnvParams($config);

    $class = "{$app_name}\\{$event_server_file_name}";
    $http = new $class($config);
    $http->start();
}

function startWebsocket($app_name, $event_server_file_name = 'WebsocketEventServer') {
    $path = START_DIR_ROOT."/protocol/{$app_name}";
    if(!is_dir($path)) {
        @mkdir($path, 0777, true);
    }

    foreach(SWOOLEFY_ENVS as $env) {
        $config_file = $path."/config-{$env}.php";
        if(!file_exists($config_file)) {
            copy(SCORE_DIR_ROOT.'/score/Websocket/config.php', $config_file);
        }
    }

    $event_server_file = START_DIR_ROOT."/{$app_name}/{$event_server_file_name}.php";
    if(!file_exists($event_server_file)) {
        $search_str = "protocol\\websocket";
        $replace_str = "{$app_name}";
        $file_content_string = file_get_contents(SCORE_DIR_ROOT."/score/EventServer/{$event_server_file_name}.php");
        $count = 1;
        $file_content_string = str_replace($search_str, $replace_str, $file_content_string,$count);
        file_put_contents($event_server_file, $file_content_string);
    }

    $autoloader_file = START_DIR_ROOT."/{$app_name}/autoloader.php";
    if(!file_exists($autoloader_file)) {
        $search_str = "<{APP_NAME}>";
        $replace_str = "{$app_name}";
        $file_content_string = file_get_contents(SCORE_DIR_ROOT."/autoloader.php");
        $count = 1;
        $file_content_string = str_replace($search_str, $replace_str, $file_content_string,$count);
        file_put_contents($autoloader_file, $file_content_string);
        include_once $autoloader_file;
    }

    $config = include $path."/config-".SWOOLEFY_ENV.".php";

    if(isset($config['setting']['log_file'])) {
        $path = pathinfo($config['setting']['log_file'], PATHINFO_DIRNAME);
        if(!is_dir($path)) {
            mkdir($path, 0777, true);
        }
    }

    if(isDaemon()) {
        $config['setting']['daemonize'] = true;
    }

    if(!isset($config['app_conf'])) {
        write("Error:protocol/{$app_name}/config-".SWOOLEFY_ENV.".php"." must include app_conf file and set app_conf");
        exit();
    }

    setEnvParams($config);

    $class = "{$app_name}\\{$event_server_file_name}";
    $websocket = new $class($config);
    $websocket->start();
}

function startRpc($app_name, $event_server_file_name = 'RpcServer') {
    $path = START_DIR_ROOT."/protocol/{$app_name}";
    if(!is_dir($path)) {
        @mkdir($path, 0777, true);
    }

    foreach(SWOOLEFY_ENVS as $env) {
        $config_file = $path."/config-{$env}.php";
        if(!file_exists($config_file)) {
            copy(SCORE_DIR_ROOT.'/score/Rpc/config.php', $config_file);
        }
    }

    $event_server_file = START_DIR_ROOT."/{$app_name}/{$event_server_file_name}.php";
    if(!file_exists($event_server_file)) {
        $search_str = "protocol\\rpc";
        $replace_str = "{$app_name}";
        $file_content_string = file_get_contents(SCORE_DIR_ROOT."/score/EventServer/{$event_server_file_name}.php");
        $count = 1;
        $file_content_string = str_replace($search_str, $replace_str, $file_content_string,$count);
        file_put_contents($event_server_file, $file_content_string);
    }

    $autoloader_file = START_DIR_ROOT."/{$app_name}/autoloader.php";
    if(!file_exists($autoloader_file)) {
        $search_str = "<{APP_NAME}>";
        $replace_str = "{$app_name}";
        $file_content_string = file_get_contents(SCORE_DIR_ROOT."/autoloader.php");
        $count = 1;
        $file_content_string = str_replace($search_str, $replace_str, $file_content_string,$count);
        file_put_contents($autoloader_file, $file_content_string);
        include_once $autoloader_file;
    }

    $config = include $path."/config-".SWOOLEFY_ENV.".php";

    if(isset($config['setting']['log_file'])) {
        $path = pathinfo($config['setting']['log_file'], PATHINFO_DIRNAME);
        if(!is_dir($path)) {
            mkdir($path, 0777, true);
        }
    }

    if(isDaemon()) {
        $config['setting']['daemonize'] = true;
    }

    if(!isset($config['app_conf'])) {
        write("Error:protocol/{$app_name}/config-".SWOOLEFY_ENV.".php"." must include app_conf file and set app_conf");
        exit();
    }

    setEnvParams($config);

    $class = "{$app_name}\\{$event_server_file_name}";
    $rpc = new $class($config);
    $rpc->start();
}

function startUdp($app_name, $event_server_file_name = 'UdpEventServer') {
    $path = START_DIR_ROOT."/protocol/{$app_name}";
    if(!is_dir($path)) {
        @mkdir($path, 0777, true);
    }

    foreach(SWOOLEFY_ENVS as $env) {
        $config_file = $path."/config-{$env}.php";
        if(!file_exists($config_file)) {
            copy(SCORE_DIR_ROOT.'/score/Udp/config.php', $config_file);
        }
    }

    $event_server_file = START_DIR_ROOT."/{$app_name}/{$event_server_file_name}.php";
    if(!file_exists($event_server_file)) {
        $search_str = "protocol\\udp";
        $replace_str = "{$app_name}";
        $file_content_string = file_get_contents(SCORE_DIR_ROOT."/score/EventServer/{$event_server_file_name}.php");
        $count = 1;
        $file_content_string = str_replace($search_str, $replace_str, $file_content_string,$count);
        file_put_contents($event_server_file, $file_content_string);
    }

    $autoloader_file = START_DIR_ROOT."/{$app_name}/autoloader.php";
    if(!file_exists($autoloader_file)) {
        $search_str = "<{APP_NAME}>";
        $replace_str = "{$app_name}";
        $file_content_string = file_get_contents(SCORE_DIR_ROOT."/autoloader.php");
        $count = 1;
        $file_content_string = str_replace($search_str, $replace_str, $file_content_string,$count);
        file_put_contents($autoloader_file, $file_content_string);
        include_once $autoloader_file;
    }

    $config = include $path."/config-".SWOOLEFY_ENV.".php";

    if(isset($config['setting']['log_file'])) {
        $path = pathinfo($config['setting']['log_file'], PATHINFO_DIRNAME);
        if(!is_dir($path)) {
            mkdir($path, 0777, true);
        }
    }

    if(isDaemon()) {
        $config['setting']['daemonize'] = true;
    }
    
    if(!isset($config['app_conf'])) {
        write("Error:protocol/{$app_name}/config-".SWOOLEFY_ENV.".php"." must include app_conf file and set app_conf");
        exit();
    }

    setEnvParams($config);

    $class = "{$app_name}\\{$event_server_file_name}";
    $udp = new $class($config);
    $udp->start();
}

function stopServer($app_name) {
    $path = START_DIR_ROOT."/protocol/{$app_name}";

    $config = include $path.'/config-'.SWOOLEFY_ENV.'.php';

    if(isset($config['setting']['pid_file'])) {
        $path = pathinfo($config['setting']['pid_file'], PATHINFO_DIRNAME);
        if(!is_dir($path)) {
            mkdir($path, 0777, true);
        }
    }

    $pid_file = $config['setting']['pid_file'];

    if(!is_file($pid_file)) {
        write("[error] pid file {$pid_file} is not exist, please check the server whether or not running");
        return;
    }

    $pid = intval(file_get_contents($pid_file));
    if(!\Swoole\Process::kill($pid, 0)){
        write("[error] pid={$pid} not exist");
        return;
    }

    \Swoole\Process::kill($pid, SIGTERM);
    \Swoole\Process::wait();
    // 如果'reload_async' => true,，则默认workerStop有30s的过度期停顿这个时间稍微会比较长，设置成60过期
    $nowtime = time();
    write("
        server begin to stopping at ".date("Y-m-d H:i:s").". please wait a moment..."
    );
    while(true) {
        sleep(1);
        if(!\Swoole\Process::kill($pid, 0)) {
            write("
        ---------------------stop info-------------------\n    
        server stop  successful. server stop at ".date("Y-m-d H:i:s")
            );
            @unlink($pid_file);
            break;
        }else {
            if(time() - $nowtime > 60){
                write("---------------------------stop info-----------------------");
                write("Please use 'ps -ef | grep php' checkout swoole whether or not stop");
                break;
            }
        }
    }
    \Swoole\Process::wait();
    exit();
}

function reloadServer($app_name) {
    global $argv;
    $path = START_DIR_ROOT.'/protocol/'.$app_name;
    $config = include $path.'/config-'.SWOOLEFY_ENV.'.php';
    if(isset($config['setting']['pid_file'])) {
        $path = pathinfo($config['setting']['pid_file'], PATHINFO_DIRNAME);
        if(!is_dir($path)) {
	    mkdir($path, 0777, true);
        }
    }
    $pid_file = $config['setting']['pid_file'];
    
    if(!is_file($pid_file)) {
        write("[error] pid file {$pid_file} is not exist, please check server is running");
        return;
    }

    $pid = intval(file_get_contents($pid_file));
    if(!\Swoole\Process::kill($pid, 0)){
        write("[error] pid={$pid} not exist");
        return;
    }
    // 发送信号，reload只针对worker进程
    \Swoole\Process::kill($pid, SIGUSR1);
    write(
        "server worker process begin to reload at ".date("Y-m-d H:i:s").". please wait a moment..."
    );
    sleep(2);
    write(
        "server worker process reload successful at ".date("Y-m-d H:i:s"),
        'light_green'
    );
    
}

function help($command) {
    switch(strtolower($command.'-'.'help')) {
        case 'start-help':{
            write("------------swoolefy启动服务命令------------",'light_green');
            showAppHelp('start','启动');
            echo "\n";
            break;
        }
        case 'stop-help':{
            write("------------swoolefy终止服务命令------------",'light_green');
            showAppHelp('stop','终止');
            echo "\n";
            break;
        }
	
        case 'reload-help':{
            write("------------swoolefy平滑重启服务命令------------",'light_green');
            showAppHelp('reload','平滑重启');
            echo "\n";
            break;
        }

        case 'create-help':{
            write("------------swoolefy平滑重启服务命令------------",'light_green');
            showAppHelp('create','创建');
            echo "\n";
            break;
        }
        default:{
            write("------------欢迎使用swoolefy------------",'light_green');
            write("有关某个命令的详细信息，请键入 help 命令:",'light_green');
            write("1、php swoolefy create help 查看创建项目详细信息!",'light_green');
            write("2、php swoolefy start help 查看启动服务详细信息!",'light_green');
            write("3、php swoolefy stop help 查看终止服务详细信息!",'light_green');
            write("4、php swoolefy reload help 查看平滑重启服务详细信息!",'light_green');
        }
    }
}

function showAppHelp($cmd, $cmd_info){
    $index = 1;
    global $APP_NAMES;
    $env_str = '-ENV';
    if('create' == $cmd){
        $env_str = '';
    }
    foreach ($APP_NAMES as $app_name => $service){
        $help_str = $index."、php swoolefy {$cmd} {$app_name} {$env_str} {$cmd_info} {$service}服务的{$app_name}项目";
        write($help_str,'light_green');
        $index++;
    }
    if($env_str){
        write("{$index}、ENV变量提供".join(SWOOLEFY_ENVS,' ').'需要更多环境变量请修改SWOOLEFY_ENVS','light_green');
        $index++;
        write("{$index}、-ENV可以省略，缺省以dev执行 对应不同的配置文件",'light_green');
    }
    $index++;
    write("{$index}、更多服务请修改\$APP_NAMES变量",'light_green');
}

function commandHandler(){
    $command = commandParser();
    if(isset($command['app_name']) && $command['app_name'] != 'help') {
        switch($command['command']){
            case 'create': {
                createProject($command['app_name']);
                break;
            }
            case "start":{
                startServer($command['app_name']);
                break;
            }
            case 'stop':{
                stopServer($command['app_name']);
                break;
            }
            case 'reload':{
                reloadServer($command['app_name']);
                break;
            }
            case 'help':
            default:{
                help($command['command']);
            }
        }
    }else {
        help($command['command']);
    }
}

function write($msg, $foreground = "red", $background = "black") {
    // Create new Colors class
    static $colors;
    if(!isset($colors)) {
        $colors = new \Swoolefy\Tool\EachColor();
    }
    echo $colors->getColoredString($msg, $foreground, $background) . "\n\n";
}

function setEnvParams($config) {
    global $argv;
    $params = array_flip($argv);
    if(isset($params['-h'])) {
        $h = $argv[$params['-h'] + 1];
        putenv("HOST_IP={$h}");
    }
    if(isset($config['port'])) {
        $port = $config['port'];
        putenv("HOST_PORT={$port}");
    }
    
    putenv("swoolefy_env=".SWOOLEFY_ENV);
}

function isDaemon() {
    global $argv;
    $params = array_flip($argv);
    if(isset($params['-d']) || isset($params['-D'])) {
        return true;
    }
    return false;
}

function showLogo() {
$logo = 
<<<LOGO
  ______                                _           _ _ _ _
 /  ____|                              | |         |  _ _ _|  _   _
|  (__     __      __   ___     ___    | |   ___   | |       | | | |
 \___  \   \ \ /\ / /  / _ \   / _ \   | |  / _ \  | |_ _ _  | | | |
 ____)  |   \ V  V /  | (_) | | (_) |  | | | ___/  |  _ _ _| | |_| |
|_____ /     \_/\_/    \___/   \___/   |_|  \___|  | |        \__, |
                                                   |_|           | |
                                                              __ / |
                                                             |_ _ /
LOGO;
write($logo,'light_green');
}

function getDefultConfig() {
$config_content = 
<<<EOF
<?php
// 应用配置
return [
    'components' => [
        'log' => [
            'is_delay' => true,
            'class' => \Swoolefy\Tool\Log::class,
            'channel' => 'application',
            'logFilePath' => rtrim(LOG_PATH,'/').'/runtime.log'
        ]
    ]
];

EOF;
return $config_content;
}

function getDefaultController() {
    $APP_NAME = APP_NAME;
    $controller_content = 
<<<EOF
<?php
namespace {$APP_NAME}\Controller;

use Swoolefy\Core\Application;
use Swoolefy\Core\Controller\BController;

class IndexController extends BController {
    public function index() {
        Application::getApp()->response->write('<h1>Hello, Welcome to Swoolefy Framework! <h1>');
    }
}
EOF;
return $controller_content;
}

function getDefines() {
    $APP_NAME = APP_NAME;
    $defines_content = 
<<<EOF
<?php
defined('APP_NAME') or define('APP_NAME', "{$APP_NAME}");
defined('APP_PATH') or define('APP_PATH',dirname(__DIR__));
defined('ROOT_PATH') or define('ROOT_PATH',dirname(APP_PATH));

// 日志目录
define('LOG_PATH', APP_PATH.'/Log');

// 定义smarty(需要安装smarty)
define('SMARTY_TEMPLATE_PATH',APP_PATH.'/View/');
define('SMARTY_COMPILE_DIR',APP_PATH.'/Runtime/');
define('SMARTY_CACHE_DIR',APP_PATH.'/Runtime/');
EOF;
return $defines_content;
}

function getDefaultModel() {
    $APP_NAME = APP_NAME;
    $defines_content = 
<<<EOF
<?php
namespace {$APP_NAME}\Model;

use Swoolefy\Core\Model\BModel;

class IndexModel extends BModel {
    public function index() {
        return "welcome to model";
    }
}
EOF;
return $defines_content;
}

initCheck();
showLogo();
commandHandler();